﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Text;
using System.Windows.Forms;
using Ndo;
using Ndo.Maths;
using Ndo.Maths.MOP;

namespace MOP
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            nsga2();
        }

        public string strMOEA = "NSGA2";
        public string strCD = "CD";
        public string strBM = "SCH";
        public BenchMark<double[]> benchmark = new SCH();
        public MOEA<XY<double[], double[]>, double[]> mop;
        
        private void dmea()
        {
            DMEA<XY<double[], double[]>, double[]> m;
            mop = m = new DMEA<XY<double[], double[]>, double[]>();
        }
        private void nsga2()
        {
            NSGA2<XY<double[], double[]>, double[]> m;
            mop = m = new NSGA2<XY<double[], double[]>, double[]>();
        }
        private void spea()
        {
            SPEA<XY<double[], double[]>, double[]> m;
            mop = m = new SPEA<XY<double[], double[]>, double[]>();
        }
        private void spea2()
        {
            SPEA2<XY<double[], double[]>, double[]> m;
            mop = m = new SPEA2<XY<double[], double[]>, double[]>();
        }

        private int start = -1;
        private void button1_Click(object sender, EventArgs e)
        {
            mop.F = benchmark.F;
            mop.Initialization =
                new Generating<double[]>(
                    new MOEA<XY<double[], double[]>, double[]>.RandomInitialization(benchmark.LowerBound,
                                                                                    benchmark.UpperBound).Generate)
                    .Array;
            mop.CrossoverRate = (double)numericUpDown3.Value;
            mop.MutationRate = (double)numericUpDown4.Value;
            mop.ScaleLimit = (int)numericUpDown2.Value;
            mop.Evaluation = Evaluation;
            mop.Preprocess();
            bmm = new BenchMarkMetric<double[]>(benchmark, mop.Population.Count * 10);
            dict = new Dictionary<P2, object>();
            start = Environment.TickCount;
            timer1.Enabled = true;
        }
        BenchMarkMetric<double[]> bmm;

        private void doST()
        {
            this.richTextBox1.AppendText(
                string.Format("[{2} {3} {4}]({0}):{1} ms\r\n", mop.GenerationNumber, Environment.TickCount - start, strBM, strMOEA, strCD));
            ST st = new ST();
            List<double[]> list =
                new List<double[]>(MOEA<XY<double[], double[]>, double[]>.EnumerateYs(mop.Population));
            if (bmm == null)
                bmm = new BenchMarkMetric<double[]>(benchmark, mop.Population.Count * 10);
            if (bmm.YOptimums != null)
            {
                st.Add(bmm.ConvergenceDistances(list));
                this.richTextBox1.AppendText("Convergence : ");
                this.richTextBox1.AppendText(st.ToString());
                this.richTextBox1.AppendText("\r\n");
                st.Reset();
            }
            st.Add(bmm.Deltas(list));
            this.richTextBox1.AppendText("Delta : ");
            this.richTextBox1.AppendText(st.ToString());
            this.richTextBox1.AppendText("\r\n");
        }

        private class P2
        {
            public P2(double[] d)
            {
                x = d;
            }
            public double[] x;
            public override int GetHashCode()
            {
                return x[0].GetHashCode()^ x[1].GetHashCode();
            }
            public override bool Equals(object obj)
            {
                double[] x1 = ((P2) obj).x;
                return x1[0] == x[0] && x1[1] == x[1];
            }
        }
        private Dictionary<P2, object> dict;
        private void timer1_Tick(object sender, EventArgs e)
        {
            if (checkBox1.Checked)
            {
                this.richTextBox1.Select(this.richTextBox1.TextLength, 0);
                this.richTextBox1.AppendText(
                    string.Format("[{0}]:{1}ms\r\n", mop.GenerationNumber, Environment.TickCount - start));
            }
            timer1.Enabled = false;
            if (mop.GenerationNumber >= (int)numericUpDown1.Value)
            {
                doST();
                start = -1;
                System.IO.StreamWriter sw = new StreamWriter("zdt4.txt", true);
                sw.WriteLine("%{0} {1} {2}", strBM, strMOEA, strCD);
                sw.WriteLine("data = [");
                foreach (KeyValuePair<P2, object> pair in dict)
                {
                    sw.Write("{0},{1}\n", pair.Key.x[0], pair.Key.x[1]);
                }
                sw.WriteLine("];");
                sw.Close();
                return;
            }
            foreach (XY<double[], double[]> xy in mop.Population)
            {
                dict[new P2(xy.Y)] = null;
            }
            mop.Process();

            //if ((mop.GenerationNumber % 5) == 0)
            //    doST();
            this.splitContainer1.Panel1.Refresh();
            timer1.Enabled = true;
        }
        private double z
        {
            get
            {
                return (double) this.numericUpDown7.Value;
            }
        }
        public readonly static double sq3 = Math.Pow(3, 0.5)/2;
        public Point f3(params double[] x)
        {
            return new Point(300 + (int)((x[0] - ((double)numericUpDownX.Value)) * z * sq3 + (x[1] - ((double)numericUpDownY.Value)) * z * (-sq3)),
                300 + (int)((x[0] - ((double)numericUpDownX.Value)) * z * 0.5 + (x[1] - ((double)numericUpDownY.Value)) * z * 0.5 - (x[2] - ((double)numericUpDownZ.Value)) * z));
        }


        private void Form1_DoubleClick(object sender, EventArgs e)
        {
            timer1.Enabled = !timer1.Enabled;
        }

        private void radioButton1_CheckedChanged(object sender, EventArgs e)
        {
            timer1.Enabled = false;
            RadioButton rb = (RadioButton)sender;
            Evaluation = Evaluations[int.Parse(rb.Tag.ToString())];
            strCD = rb.Text;
        }

        static CrowdingEvaluation<double[]>[] Evaluations = new CrowdingEvaluation<double[]>[]
                {
                    null,
                    new CrowedingDistanceEvaluation<double[]>(Measure.EuclideanDistance),
                    new KthNeighborCrowdingEvaluation<double[]>(Measure.EuclideanDistance),
                    new GradientCrowdingEvaluation<double[]>(GradientFunctions.PowerK2, Measure.EuclideanDistance),
                    new GradientCrowdingEvaluation<double[]>(GradientFunctions.Power2K, Measure.EuclideanDistance),
                    new GradientCrowdingEvaluation<double[]>(GradientFunctions.Power3K, Measure.EuclideanDistance),
                    new GradientCrowdingEvaluation<double[]>(GradientFunctions.Factorial, Measure.EuclideanDistance),
                    null
                };

        private CrowdingEvaluation<double[]> Evaluation = Evaluations[1];
        private void radioButton2_CheckedChanged(object sender, EventArgs e)
        {
            timer1.Enabled = false;
            RadioButton rb = (RadioButton) sender;
            if (rb.Text == "NSGA2")
                nsga2();
            if (rb.Text == "SPEA2")
                spea2();
            if (rb.Text == "SPEA")
                spea();
            if (rb.Text == "DMEA")
                dmea();
            strMOEA = rb.Text;
        }

        private void radioButton10_CheckedChanged(object sender, EventArgs e)
        {
            timer1.Enabled = false;
            RadioButton rb = (RadioButton)sender;
            benchmark = BenchMarks.FromName(rb.Text);
            strBM = rb.Text;
        }

        private void splitContainer1_Panel1_Paint(object sender, PaintEventArgs e)
        {
            draw(e.Graphics);
        }

        private void draw(Graphics g)
        {
            if (mop == null || mop.GenerationNumber < 0)
                return;
            //BenchMarkMetric<double[]> bmm = null;
            if(checkBox2.Checked)
            {
                if(bmm == null)
                    bmm = new BenchMarkMetric<double[]>(benchmark, mop.Population.Count * 10);
            }
            else
            {
                bmm = null;
            }

            if (mop.Population[0].Y.Length > 2)
            {
                g.DrawLine(Pens.Black, 300, 0, 300, 300);
                g.DrawLine(Pens.Black, 300 + (int)(300 * sq3), 450, 300, 300);
                g.DrawLine(Pens.Black, 300 - (int)(300 * sq3), 450, 300, 300);

                Point[][] pp = new Point[10][];

                for (int i = 0; i < pp.Length; i++)
                {
                    Point[] p3 = new Point[] { f3(i, 0, 0), f3(0, i, 0), f3(0, 0, i) };
                    pp[i] = p3;
                    Pen p = new Pen(Color.FromArgb(i * 25, i * 25, i * 25));
                    p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
                    p.DashPattern = new float[2] { i + 1, i + 1 };
                    g.DrawLine(p, p3[0], p3[1]);
                    g.DrawLine(p, p3[1], p3[2]);
                    g.DrawLine(p, p3[0], p3[2]);
                }

                foreach (XY<double[], double[]> xy in mop.Population)
                {
                    Point p = f3(xy.Y);
                    double t = xy.Y[0] + xy.Y[1] + xy.Y[2];
                    int a =  + (int)t;
                    g.DrawRectangle(Pens.Red, p.X - (a / 2), p.Y - (a / 2), a, a);
                }
                if (bmm != null && bmm.YOptimums != null)
                {
                    foreach (double[] optimum in bmm.YOptimums)
                    {
                        Point p = f3(optimum);
                        int a = 1;
                        g.DrawRectangle(Pens.Blue, p.X - (a/2), p.Y - (a/2), a, a);
                    }
                }
            }
            else
            {
                //g.TranslateTransform(-70, -70);
                //g.DrawString("0.0", this.Font, Brushes.Black, 100 - 10, 100 - 15);
                //g.DrawString("0.0", this.Font, Brushes.Black, 100 - 25, 300 - 5);
                //g.DrawLine(Pens.Black, 100 + 2, 300, 100 - 2, 300);
                //g.DrawLine(Pens.Black, 100, 100, 500, 100);
                //g.DrawLine(Pens.Black, 100, 100, 100, 500);

                //for (int i = 1; i <= 10; i++)
                //{
                //    string s = (i * 2).ToString().PadLeft(2, '0'), ss = i.ToString().PadLeft(2, '0');
                //    s = s[0] + "." + s[1];
                //    ss = ss[0] + "." + ss[1];
                //    g.DrawString(s, this.Font, Brushes.Black, 100 + i * 40 - 10, 100 - 15);
                //    g.DrawLine(Pens.Black, 100 + i * 40, 100 + 2, 100 + i * 40, 100 - 2);
                //    g.DrawString(ss, this.Font, Brushes.Black, 100 - 25, 300 + i * 20 - 5);
                //    g.DrawLine(Pens.Black, 100 + 2, 300 + i * 20, 100 - 2, 300 + i * 20);
                //    g.DrawString("-" + ss, this.Font, Brushes.Black, 100 - 30, 300 - i * 20 - 5);
                //    g.DrawLine(Pens.Black, 100 + 2, 300 - i * 20, 100 - 2, 300 - i * 20);
                //}

                
                g.TranslateTransform(-70, -70);
                g.DrawString("0.0", this.Font, Brushes.Black, 100 - 20, 100 - 15);
                g.DrawLine(Pens.Black, 100, 100, 500, 100);
                g.DrawLine(Pens.Black, 100, 100, 100, 500);

                for (int i = 1; i <= 10; i++ )
                {
                    string s = i.ToString().PadLeft(2, '0');
                    s = s[0] + "." + s[1];
                    g.DrawString(s, this.Font, Brushes.Black, 100+i*40-10, 100-15);
                    g.DrawLine(Pens.Black, 100+i*40, 100+2, 100+i*40, 100-2);
                    g.DrawString(s, this.Font, Brushes.Black, 100 - 25, 100 + i * 40 - 5);
                    g.DrawLine(Pens.Black, 100 + 2, 100 + i * 40, 100 - 2, 100 + i * 40);
                }

                foreach (XY<double[], double[]> xy in mop.Population)
                {
                    if (double.IsNaN(xy.Y[0]) || double.IsNaN(xy.Y[1]))
                        continue;
                    Point p = new Point();
                    p.X = (int) ((xy.Y[0] - ((double) numericUpDownX.Value))*z);
                    p.Y = (int) ((xy.Y[1] - ((double) numericUpDownY.Value))*z);
                    int a = 1;
                    g.DrawRectangle(Pens.Red, 100 + p.X - (a/2), 100 + p.Y - (a/2), a, a);
                }
                if (bmm != null && bmm.YOptimums != null)
                {
                    List<Point> ps = new List<Point>();
                    foreach (double[] optimum in bmm.YOptimums)
                    {
                        Point p = new Point();
                        p.X = 100 + (int) ((optimum[0] - ((double) numericUpDownX.Value))*z);
                        p.Y = 100 + (int) ((optimum[1] - ((double) numericUpDownY.Value))*z);
                        ps.Add(p);
                    }
                    g.DrawLines(Pens.Blue, ps.ToArray());
                }
            }
        }

        private void numericUpDown7_ValueChanged(object sender, EventArgs e)
        {
            this.splitContainer1.Panel1.Refresh();
        }

        private void checkBox2_CheckedChanged(object sender, EventArgs e)
        {
            this.splitContainer1.Panel1.Refresh();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            if(this.saveFileDialog1.ShowDialog(this) == System.Windows.Forms.DialogResult.OK)
            {
                Bitmap bm = new Bitmap(460,460);
                Graphics g = Graphics.FromImage(bm);
                g.FillRectangle(Brushes.White, new Rectangle(new Point(0,0), bm.Size));
                draw(g);
                System.IO.Stream sr = this.saveFileDialog1.OpenFile();
                bm.Save(sr, System.Drawing.Imaging.ImageFormat.Jpeg);
                sr.Close();
            }
        }
    }
}